package com.ruoyi.worker.service.v2.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableTable;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.worker.constant.AttendanceSignResultEnum;
import com.ruoyi.worker.domain.v2.RzAttendanceSumDailyRecordDetailV2Domain;
import com.ruoyi.worker.domain.v2.RzAttendanceSumDailyRecordV2Domain;
import com.ruoyi.worker.domain.workAttendance.RzScheduleStaffPlan;
import com.ruoyi.worker.mapper.TermAttendanceGroupV2Mapper;
import com.ruoyi.worker.mapper.TermAttendanceRulesV2Mapper;
import com.ruoyi.worker.mapper.attendance.AttendanceDailyRecordMapper;
import com.ruoyi.worker.mapper.attendance.RzScheduleStaffPlanMapper;
import com.ruoyi.worker.mapper.v2.RzAttendanceSumDailyRecordDetailHistoryV2Mapper;
import com.ruoyi.worker.mapper.v2.RzAttendanceSumDailyRecordDetailV2Mapper;
import com.ruoyi.worker.mapper.v2.RzAttendanceSumDailyRecordV2Mapper;
import com.ruoyi.worker.mapper.v2.RzAttendanceSumMonthMapper;
import com.ruoyi.worker.service.v2.RzAttendanceSumDailyRecordV2Service;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

@Service
public class RzAttendanceSumDailyRecordV2ServiceImpl implements RzAttendanceSumDailyRecordV2Service {

    @Resource
    private RzScheduleStaffPlanMapper staffPlanMapper;
    @Resource
    private AttendanceDailyRecordMapper workAttendanceMapper;
    @Resource
    private TermAttendanceGroupV2Mapper groupV2Mapper;
    @Resource
    private TermAttendanceRulesV2Mapper rulesV2Mapper;

    @Resource
    private RzAttendanceSumDailyRecordDetailHistoryV2Mapper historyV2Mapper;
    @Resource
    private RzAttendanceSumDailyRecordDetailV2Mapper detailV2Mapper;
    @Resource
    private RzAttendanceSumDailyRecordV2Mapper recordV2Mapper;

    private List<RzScheduleStaffPlan> staffPlans; // 排班信息
    private Map<String, List<Date>> signRecordMap; // 打卡信息
    private Map<Integer, Map<String, String>> groupMap; // 考勤组
    private Map<Integer, Integer> rules; // 打卡规则
    private Map<Integer, String> shiftNameMap; // 启用状态的考勤组名称
    private Map<String, List<RzAttendanceSumDailyRecordV2Domain>> recordMap; // 记录集，每天每个通班会有两条记录，单班只有一条记录
    private List<RzAttendanceSumDailyRecordV2Domain> wait4SaveRecords; // 待存汇总数据

    private int[] tmp = {11, 12, 13, 21};

    @Override
    @Transactional
    public void buildSumDailyRecordWithDate(String date) {
        if (detailV2Mapper.exist(date) > 0) {
            // 每个日期只生成一次考勤记录
            return;
        }
        long node1 = System.nanoTime();
        groupMap = new HashMap<>();
        signRecordMap = new HashMap<>();
        shiftNameMap = new HashMap<>();
        recordMap = new HashMap<>();
        rules = new HashMap<>();
        wait4SaveRecords = new ArrayList<>();
        rules.put(0, 0);
        rules.put(-1, 480);
        // 获取指定日期的排班记录和打卡记录
        this.staffPlansAndSignRecords(date);
        if (this.staffPlans.isEmpty() || this.signRecordMap.isEmpty() || this.groupMap.isEmpty() || this.rules.isEmpty()) {
            // 任意一个为空都无法生成日考勤
            return;
        }
        // 保存员工日考勤打卡记录详情
        long node2 = System.nanoTime();
        this.saveSumRecordDetail();
        long node3 = System.nanoTime();
        System.out.println("准备工作：" + (node2 - node1));
        System.out.println("主要算法：" + (node3 - node2));

    }

    @Transactional
    void saveSumRecordDetail() {
        List<RzAttendanceSumDailyRecordDetailV2Domain> wait4SaveDetailRecord = new ArrayList<>();
        Map<Long, List<RzAttendanceSumDailyRecordV2Domain>> map = new HashMap<>();
        for (RzScheduleStaffPlan item : this.staffPlans) {
            Map<String, String> result = this.groupMap.get(Math.toIntExact(item.getAttendanceGroupId()));
            if (ObjectUtils.isEmpty(result)) {
                // 未知的排班组，不应该存在的分支
                continue;
            }
            // 排班名称
            String name = result.get("name");
            if (StringUtils.isEmpty(name)) {
                // 没有排班信息,正常应该是已离职情况，但是无法判定
                continue;
            }
            Integer checkResult = this.validIsUnWork(name);

            if (!ObjectUtils.isEmpty(checkResult)) {
                // 未出勤情况，目前只有休息和离职两种情况，但是这两种都不能用于统计，没有归属
                for (int i : tmp) {
                    wait4SaveDetailRecord.add(this.setRecordDetailInfo(item, null, i, checkResult));
                }
                continue;
            }
            JSONArray signConditions = JSONObject.parseArray(result.get("signItems"));
            /*
                if 考勤组id对应名称 == '通班':
                    通班考勤判定([通班白班entity,通班夜班entity])
                else:
                    单班考勤判定(白班entity|夜班entity)
             */
            List<RzAttendanceSumDailyRecordV2Domain> domains = map.get(item.getAttendanceGroupId());
            if (CollectionUtil.isEmpty(domains)) {
                domains = this.getRecord(item);
                map.put(item.getAttendanceGroupId(), domains);
            }
            if (domains.size() == 1) {
                // 单班
                wait4SaveDetailRecord.addAll(this.validWorkSingle(item, signConditions, domains.get(0), name));
            } else {
                // 通班
                wait4SaveDetailRecord.addAll(this.validWorkCross(item, signConditions, domains.get(0), domains.get(1)));
            }
        }
        for (List<RzAttendanceSumDailyRecordV2Domain> v : map.values()) {
            wait4SaveRecords.addAll(v);
        }
        if (wait4SaveDetailRecord.size() > 0 && wait4SaveRecords.size() > 0) {
            detailV2Mapper.multiInsert(wait4SaveDetailRecord); // 存打卡详情
            recordV2Mapper.multiInsert(wait4SaveRecords); // 存统计记录
        }
    }


    private List<RzAttendanceSumDailyRecordV2Domain> getRecord(RzScheduleStaffPlan plan) {
        Integer attendanceGroupId = Math.toIntExact(plan.getAttendanceGroupId());
        List<RzAttendanceSumDailyRecordV2Domain> records = recordMap.get(attendanceGroupId);
        if (!ObjectUtils.isEmpty(records)) {
            return records;
        }
        records = new ArrayList<>();
        String shiftName = shiftNameMap.get(attendanceGroupId);
        if (StringUtils.equals(shiftName, AttendanceSignResultEnum.SIGN_WORK_TYPE_DAY_WORK.getName())) {
            // 白班
            RzAttendanceSumDailyRecordV2Domain domain = new RzAttendanceSumDailyRecordV2Domain(plan.getId(),
                    AttendanceSignResultEnum.SIGN_WORK_TYPE_DAY_WORK.getCode(), plan.getScheduleDate(), attendanceGroupId,
                    plan.getDeptId());
            domain.setStatus(0);
            records.add(domain);
        } else if (StringUtils.equals(shiftName, AttendanceSignResultEnum.SIGN_WORK_TYPE_NIGHT_WORK.getName())) {
            // 夜班
            RzAttendanceSumDailyRecordV2Domain domain = new RzAttendanceSumDailyRecordV2Domain(plan.getId(),
                    AttendanceSignResultEnum.SIGN_WORK_TYPE_NIGHT_WORK.getCode(), plan.getScheduleDate(), attendanceGroupId,
                    plan.getDeptId());
            domain.setStatus(0);
            records.add(domain);
        } else {
            // 通班，需要白天和晚上两条
            RzAttendanceSumDailyRecordV2Domain domain1 = new RzAttendanceSumDailyRecordV2Domain(plan.getId(),
                    AttendanceSignResultEnum.SIGN_WORK_TYPE_DAY_WORK.getCode(), plan.getScheduleDate(), attendanceGroupId,
                    plan.getDeptId());
            RzAttendanceSumDailyRecordV2Domain domain2 = new RzAttendanceSumDailyRecordV2Domain(plan.getId(),
                    AttendanceSignResultEnum.SIGN_WORK_TYPE_NIGHT_WORK.getCode(), plan.getScheduleDate(), attendanceGroupId,
                    plan.getDeptId());
            domain1.setStatus(0);
            domain2.setStatus(0);
            records.add(domain1);
            records.add(domain2);
        }

        return records;
    }


    /**
     * @author: 陈宇寰
     * @date: 2021-12-31 14:21:58
     * @params:
     * @return:
     * @description: 通班出勤判定
     */
    private List<RzAttendanceSumDailyRecordDetailV2Domain> validWorkCross(RzScheduleStaffPlan staffPlan, JSONArray signConditions,
                                                                          RzAttendanceSumDailyRecordV2Domain dayRecord, RzAttendanceSumDailyRecordV2Domain nightRecord) {
        String idCard = staffPlan.getIdCard();
        List<Date> signTimes = this.signRecordMap.get(idCard);
        List<RzAttendanceSumDailyRecordDetailV2Domain> data = new ArrayList<>();
        List<Integer> dayWorkResult = new ArrayList<>(); // 通班白班出勤结果
        List<Integer> nightWorkResult = new ArrayList<>(); // 通班夜班出勤结果
        if (ObjectUtils.isEmpty(signTimes)) {
            // 整日缺卡
            for (int i : tmp) {
                data.add(this.setRecordDetailInfo(staffPlan, null, i, AttendanceSignResultEnum.SIGN_RESULT_ABSENT.getCode()));
            }
            dayWorkResult.add(AttendanceSignResultEnum.SIGN_RESULT_ABSENT.getCode());
            nightWorkResult.add(AttendanceSignResultEnum.SIGN_RESULT_ABSENT.getCode());
        } else {
            int x = 10;
            for (int i = 0; i < signConditions.size(); i++) {
                JSONObject obj = signConditions.getJSONObject(i);
                Integer signType = obj.getInteger("type");
                Integer sortNum = obj.getInteger("sortNum");
                Boolean nextDay = obj.getBoolean("nextDay");

                Map<String, Object> result;
                if (signType.equals(AttendanceSignResultEnum.SIGN_TYPE_UP_WORK.getCode())) {
                    // 上班 判定：0-正常，1-普通迟到，2-一般迟到，3-严重迟到，5-旷工
                    int[] tmps = {0, 1, 2, 3, 5};
                    if (signType == 1 && ImmutableList.of(1, 2).contains(sortNum)) {
                        // 通班白班
                        result = this.charge(tmps, signTimes, staffPlan, obj, nextDay, dayRecord);
                        dayWorkResult.add((Integer) result.get("signResult"));
                    } else {
                        // 通班夜班
                        result = this.charge(tmps, signTimes, staffPlan, obj, nextDay, nightRecord);
                        nightWorkResult.add((Integer) result.get("signResult"));
                    }
                    x += 1;
                } else {
                    // 下班 判定：0-正常，4-早退
                    int[] tmps = {0, 4};
                    result = this.charge(tmps, signTimes, staffPlan, obj, nextDay, nightRecord);
                    nightWorkResult.add((Integer) result.get("signResult"));
                    x = 21;
                }

                data.add(this.setRecordDetailInfo(staffPlan, (Date) result.get("signTime"), x, (Integer) result.get("signResult")));
            }
        }

        this.setDayOrNightWorkResult(dayRecord, dayWorkResult);
        this.setDayOrNightWorkResult(nightRecord, nightWorkResult);

//        wait4SaveRecords.add(dayRecord);
//        wait4SaveRecords.add(nightRecord);
        return data;
    }

    /**
     * @author: 陈宇寰
     * @date: 2021-12-31 17:58:51
     * @params:
     * @return:
     * @description: 单班次判定，单班次只有上班和下班的概念，通班还根据打卡时间分为白班和晚班概念
     */
    private List<RzAttendanceSumDailyRecordDetailV2Domain> validWorkSingle(RzScheduleStaffPlan staffPlan, JSONArray signConditions,
                                                                           RzAttendanceSumDailyRecordV2Domain record, String name) {
        String idCard = staffPlan.getIdCard();
        List<Date> signTimes = this.signRecordMap.get(idCard);
        List<RzAttendanceSumDailyRecordDetailV2Domain> data = new ArrayList<>();
        List<Integer> workResult = new ArrayList<>(); // 出勤结果
        if (ObjectUtils.isEmpty(signTimes)) {
            int[] temps;
            if (StringUtils.equals("白班", name)) {
                // 整日缺卡
                temps = new int[]{11, 12, 21};
            } else {
                temps = new int[]{11, 21};
            }
            for (int i : temps) {
                data.add(this.setRecordDetailInfo(staffPlan, null, i, AttendanceSignResultEnum.SIGN_RESULT_ABSENT.getCode()));
            }
            workResult.add(AttendanceSignResultEnum.SIGN_RESULT_ABSENT.getCode());
        } else {
            int x = 10;

            for (int i = 0; i < signConditions.size(); i++) {
                JSONObject obj = signConditions.getJSONObject(i);
                Integer signType = obj.getInteger("type");
                Integer sortNum = obj.getInteger("sortNum");
                Boolean nextDay = obj.getBoolean("nextDay");

                Map<String, Object> result;
                if (signType.equals(AttendanceSignResultEnum.SIGN_TYPE_UP_WORK.getCode())) {
                    // 上班 判定：0-正常，1-普通迟到，2-一般迟到，3-严重迟到，5-旷工
                    int[] tmps = {0, 1, 2, 3, 5};
                    if (signType == 1) {
                        // 上班
                        result = this.charge(tmps, signTimes, staffPlan, obj, nextDay, record);
                    } else {
                        // 下班
                        result = this.charge(tmps, signTimes, staffPlan, obj, nextDay, record);
                    }
                    x += 1;
                } else {
                    // 下班 判定：0-正常，4-早退
                    int[] tmps = {0, 4};
                    result = this.charge(tmps, signTimes, staffPlan, obj, nextDay, record);
                    workResult.add((Integer) result.get("signResult"));
                    x = 21;
                }

                data.add(this.setRecordDetailInfo(staffPlan, (Date) result.get("signTime"), x, (Integer) result.get("signResult")));
            }
        }
        this.setDayOrNightWorkResult(record, workResult);
//        wait4SaveRecords.add(record);
        return data;
    }

    /**
     * @author: 陈宇寰
     * @date: 2021-12-29 16:24:09
     * @params:
     * @return:
     * @description: 统计每日白晚班实际出勤情况
     */
    private void setDayOrNightWorkResult(RzAttendanceSumDailyRecordV2Domain recordV2Domain, List<Integer> workResult) {
        /*if (workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_VACATE.getCode()) ||
                workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_LEAVE.getCode()) ||
                workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_ABSENT.getCode()) ||
                workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_ABSENTEEISM.getCode())) {
//            recordV2Domain.setResult();
        } else if (workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_NORMAL.getCode()) ||
                workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_LATE.getCode()) ||
                workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_NORMAL_LATE.getCode()) ||
                workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_SERIOUSLY_LATE.getCode()) ||
                workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_WORK_INJURE.getCode())) {
            recordV2Domain.setResult(1);
        }*/
        if (workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_NORMAL.getCode()) ||
                workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_LATE.getCode()) ||
                workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_NORMAL_LATE.getCode()) ||
                workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_SERIOUSLY_LATE.getCode()) ||
                workResult.contains(AttendanceSignResultEnum.SIGN_RESULT_WORK_INJURE.getCode())) {
            recordV2Domain.setResult(recordV2Domain.getResult() + 1);
        }
    }

    private Map<String, Object> charge(int[] tmps, List<Date> signTimes, RzScheduleStaffPlan staffPlan, JSONObject obj, Boolean nextDay, RzAttendanceSumDailyRecordV2Domain record) {
        Date signTime = null;
        Integer signResult = null;
        Map<String, Object> result = new HashMap<>();
        for (int x : tmps) {
            if (!ObjectUtils.isEmpty(signTime)) {
                break;
            }
            Date startTime = this.buildUpWorkChargeCondition(staffPlan.getScheduleDate(), obj.getString("startTime"), nextDay, 0);
            Date endTime = this.buildUpWorkChargeCondition(staffPlan.getScheduleDate(), obj.getString("endTime"), nextDay, rules.get(x));
            for (Date d : signTimes) {
                if (Objects.equals(obj.getInteger("type"), AttendanceSignResultEnum.SIGN_TYPE_UP_WORK.getCode()) && d.compareTo(startTime) >= 0 && d.compareTo(endTime) <= 0) {
                    // 上班
                    signTime = d;
                    signResult = this.signResultCharge(x, record);
                    break;
                } else if (Objects.equals(obj.getInteger("type"), AttendanceSignResultEnum.SIGN_TYPE_DOWN_WORK.getCode())) {
                    // 下班
                    if (d.compareTo(startTime) >= 0 && d.compareTo(endTime) <= 0) {
                        signTime = d;
                        signResult = this.signResultCharge(x, record);
                        break;
                    } else if (d.compareTo(startTime) < 0) {
                        long diffTime = DateUtils.getDatePoorMinutes(d, startTime);
                        if (diffTime > -90) {
                            signTime = d;
                            if (diffTime > -rules.get(4)) {
                                signResult = this.signResultCharge(4, record);
                            } else {
                                signResult = this.signResultCharge(5, record);
                            }
                            break;
                        }
                    }
                }
            }
        }
        if (ObjectUtils.isEmpty(signTime)) {
            signResult = this.signResultCharge(-1, record);
        }
        result.put("signTime", signTime);
        result.put("signResult", signResult);
        return result;
    }

    /**
     * @author: 陈宇寰
     * @date: 2021-12-26 16:20:48
     * @params:
     * @return:
     * @description: 构建判定时间区间
     */
    private Date buildUpWorkChargeCondition(Date staffDate, String workTime, Boolean nextDay, int minutes) {
        Date time;
        if (nextDay) {
            time = DateUtils.parseDate(String.format("%s %s", DateUtils.getDifferDay(staffDate, 1), workTime));
        } else {
            time = this.actualWorkDatetime(staffDate, workTime);
        }
        if (minutes != 0) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(time);
            calendar.add(Calendar.MINUTE, minutes);
            time = calendar.getTime();
        }
        return time;
    }

    private Date actualWorkDatetime(Date workDate, String workTime) {
        String wd = DateUtils.getFormatDate(workDate, "yyyy-MM-dd");
        return DateUtils.parseDate(String.format("%s %s", wd, workTime));
    }

    private RzAttendanceSumDailyRecordDetailV2Domain setRecordDetailInfo(RzScheduleStaffPlan staffPlan, Date signTime, Integer signType, Integer signResult) {
        RzAttendanceSumDailyRecordDetailV2Domain detail = new RzAttendanceSumDailyRecordDetailV2Domain();
        detail.setIdCard(staffPlan.getIdCard());
        detail.setName(staffPlan.getStaffName());
        detail.setSignTime(signTime);
        detail.setUpdateFlag(0);
        detail.setSignType(signType);
        detail.setSignResult(signResult);
        detail.setWorkDate(staffPlan.getScheduleDate());
        detail.setCreateTime(new Date());
        detail.setCreateBy("admin");
        detail.setUpdateTime(new Date());
        detail.setUpdateBy("admin");
        detail.setStatus(0);
        return detail;
    }

    /**
     * @author: 陈宇寰
     * @date: 2021-12-28 09:28:21
     * @params:
     * @return:
     * @description: 判定考勤结果
     */
    private Integer signResultCharge(int temp, RzAttendanceSumDailyRecordV2Domain record) {
        if (temp == 0) {
            // 正常
            return AttendanceSignResultEnum.SIGN_RESULT_NORMAL.getCode();
        } else if (temp == 1) {
            // 迟到
            record.setNumberOfLate(record.getNumberOfLate() + 1);
            return AttendanceSignResultEnum.SIGN_RESULT_LATE.getCode();
        } else if (temp == 2) {
            // 普通迟到
            record.setNumberOfNormallyLate(record.getNumberOfNormallyLate() + 1);
            return AttendanceSignResultEnum.SIGN_RESULT_NORMAL_LATE.getCode();
        } else if (temp == 3) {
            // 严重迟到
            record.setNumberOfSeriouslyLate(record.getNumberOfSeriouslyLate() + 1);
            return AttendanceSignResultEnum.SIGN_RESULT_SERIOUSLY_LATE.getCode();
        } else if (temp == 4) {
            // 早退
            record.setNumberOfLeaveEarly(record.getNumberOfLeaveEarly() + 1);
            return AttendanceSignResultEnum.SIGN_RESULT_LEAVE_EARLY.getCode();
        } else if (temp == 5) {
            // 旷工
            record.setNumberOfAbsenteeism(record.getNumberOfAbsenteeism() + 1);
            return AttendanceSignResultEnum.SIGN_RESULT_ABSENTEEISM.getCode();
        }
        // 缺卡
        record.setNumberOfAbnormal(record.getNumberOfAbnormal() + 1);
        return AttendanceSignResultEnum.SIGN_RESULT_ABSENT.getCode();
    }

    /**
     * @author: 陈宇寰
     * @date: 2021-12-26 14:57:37
     * @params:
     * @return:
     * @description: 检查是否是请假、休息、工伤、离职状态
     */
    private Integer validIsUnWork(String name) {
        if (StringUtils.equals(name, AttendanceSignResultEnum.SIGN_RESULT_HOLIDAY.getName())) {
            return AttendanceSignResultEnum.SIGN_RESULT_HOLIDAY.getCode();
        } else if (StringUtils.equals(name, AttendanceSignResultEnum.SIGN_RESULT_LEAVE.getName())) {
            return AttendanceSignResultEnum.SIGN_RESULT_LEAVE.getCode();
        } else if (StringUtils.equals(name, AttendanceSignResultEnum.SIGN_RESULT_VACATE.getName())) {
            return AttendanceSignResultEnum.SIGN_RESULT_VACATE.getCode();
        } else if (StringUtils.equals(name, AttendanceSignResultEnum.SIGN_RESULT_WORK_INJURE.getName())) {
            return AttendanceSignResultEnum.SIGN_RESULT_WORK_INJURE.getCode();
        }
        return null;
    }

    /**
     * @author: 陈宇寰
     * @date: 2021-12-25 23:01:23
     * @params:
     * @return:
     * @description: 获取判定考勤结果所需要的排班记录、打卡记录、考勤组规则、通用规则
     */
    private void staffPlansAndSignRecords(String date) {
        // 排班记录
        RzScheduleStaffPlan staffPlan = new RzScheduleStaffPlan().setScheduleDate(DateUtils.parseDate(date));
        staffPlans = staffPlanMapper.selectRzScheduleStaffPlanList(staffPlan);


        // 打卡记录
        workAttendanceMapper.listOfAttendancePassRecord(ImmutableMap.of("workTime", date, "afterWorkTime",
                DateUtils.getDifferDay(DateUtils.parseDate(date), 1))).forEach(item -> {
            List<Date> signTimes = this.signRecordMap.get(StringUtils.trim(item.getIdCard()));
            if (CollectionUtil.isEmpty(signTimes)) {
                signTimes = new ArrayList<>();
            }
            signTimes.add(DateUtils.parseDate(item.getDateTime()));
            this.signRecordMap.put(item.getIdCard(), signTimes);
        });

        // 考勤组
        groupV2Mapper.info4Charge().forEach(item -> {
            this.groupMap.put(Math.toIntExact(item.getId()), ImmutableMap.of("name", item.getName(), "signItems", item.getSignItems()));
            this.shiftNameMap.put(Math.toIntExact(item.getId()), item.getShiftName());
        });

        // 通用规则
        rulesV2Mapper.ruleRecords().forEach(item -> {
            this.rules.put(item.getId(), item.getMinutes());
        });

    }

    @Override
    public List<Map<String, Integer>> totalStatistics(String date, Integer groupId, Integer deptId) {
        return recordV2Mapper.totalStatistics(date, groupId, deptId);
    }

    @Override
    public ImmutableMap<String, Object> statistics(String date, Integer groupId, Integer deptId) {
        List<Map<String, Integer>> totalStatistics = recordV2Mapper.totalStatistics(date, groupId, deptId);
        if (CollectionUtil.isEmpty(totalStatistics)) {
            return null;
        }

        int[] tmp;
        Map<String, String> nameMap = groupV2Mapper.shiftNameWithGroupId(groupId);
        String name = nameMap.get("name");
        if (StringUtils.equals(name, "白班")) {
            tmp = new int[]{11, 12, 21};
        } else if (StringUtils.equals(name, "夜班")) {
            tmp = new int[]{11, 21};
        } else {
            tmp = new int[]{11, 12, 13, 21};
        }

        List<Map<String, Integer>> detailStatistics = new ArrayList<>();

        for (int i : tmp) {
            Map<String, Integer> stat = detailV2Mapper.detailStatistics(date, groupId, deptId, i);
            stat.put("workType", i);
            detailStatistics.add(stat);
        }

        return ImmutableMap.of("total", totalStatistics, "detail", detailStatistics);
    }

    @Override
    public void confirmRecord(String date, String groupId, String deptId) {
        Integer count = detailV2Mapper.countAbsent(date);
        if(count > 0){
            throw new ServiceException("请先将缺卡状态处理之后再进行确认");
        }
        recordV2Mapper.confirmAttendance(date, groupId, deptId);
    }

    @Override
    @Transactional
    public void changeIdCardWhenBaseUpdate(String oldIdCard, String newIdCard) {
        if (StringUtils.isEmpty(oldIdCard) || StringUtils.isEmpty(newIdCard)) {
            throw new ServiceException("原身份证号和现身份证号不能为空");
        }
        // 修改排班表身份证号
        staffPlanMapper.updateIdCard(oldIdCard, newIdCard);
        // 修改考勤明细身份证号
        detailV2Mapper.updateIdCardWhenBaseUpdate(oldIdCard, newIdCard);
        // 修改历史考勤明细身份证号
        historyV2Mapper.updateIdCardWhenBaseUpdate(oldIdCard, newIdCard);
    }
}
